package com.youpai.platform.user.service.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.youpai.platform.chat.service.entity.po.ChatAccountPO;
import com.youpai.platform.chat.service.mapper.ChatAccountMapper;
import com.youpai.platform.common.constant.BizConstant;
import com.youpai.platform.common.constant.NumberConstant;
import com.youpai.platform.common.enums.ProcessCodeEnum;
import com.youpai.platform.common.exceptions.BaseBizException;
import com.youpai.platform.common.util.SendEmailUtil;
import com.youpai.platform.common.util.UserContext;
import com.youpai.platform.common.util.cache.Cache;
import com.youpai.platform.common.util.token.TokenUtil;
import com.youpai.platform.user.service.entity.po.UserFollowPO;
import com.youpai.platform.user.service.enums.UserFollowStatus;
import com.youpai.platform.user.service.mapper.UserFollowMapper;
import com.youpai.platform.user.web.entity.dto.*;
import com.youpai.platform.user.service.entity.po.UserPO;
import com.youpai.platform.user.service.mapper.UserMapper;
import com.youpai.platform.user.service.service.UserService;
import com.youpai.platform.user.service.converter.UserConverter;
import com.youpai.platform.user.service.service.transaction.UserTransaction;
import com.youpai.platform.user.web.entity.req.*;
import com.youpai.platform.user.web.entity.resp.*;
import com.youpai.platform.vulture.common.enums.AppIdEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Description: 用户Service
 * @Author:welles
 * @Date:2022/7/23
 */
@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserMapper userMapper;
    @Resource
    private UserFollowMapper userFollowMapper;
    @Resource
    @Qualifier(value = "redisCache")
    private Cache<String, String> strCache;
    @Resource
    @Qualifier(value = "redisCache")
    private Cache<Long, UserPO> userCache;
    @Resource
    @Qualifier(value = "redisCache")
    private Cache<String, Long> tokenCache;
    @Resource
    private SendEmailUtil sendEmailUtil;
    @Resource
    private UserTransaction userTransaction;
    @Resource
    private ChatAccountMapper chatAccountMapper;

    @Override
    public UserLoginResp userRegister(UserRegisterReq req, HttpServletResponse resp) {
        checkVerificationCode(req.getVerificationCode());
        UserPO userPO = null;
        try {
            userPO = userTransaction.initUserAndGetUserPOByEmail(req.getUserName(), req.getPwd());
        } catch (DuplicateKeyException duplicateKeyException) {
            log.error("用户已存在：userName - {}", req.getUserName());
            throw new BaseBizException(ProcessCodeEnum.USER_IS_REPETITION);
        }
        userCache.putValue(userPO.getUid(), userPO);
        String token = TokenUtil.generateToken();
        // 缓存token
        tokenCache.putValue(token, userPO.getUid());
        resp.setHeader("w-token", token);
        UserDTO userDTO = UserConverter.convertUserPO2UserDTO(userPO);
        // 获取accId信息
        UserImDTO userImDTO = getUserImInfoByUid(userDTO.getUid());
        return new UserLoginResp().setUserInfo(buildUserInfoDTO(userDTO,userImDTO));
    }

    private UserInfoDTO buildUserInfoDTO(UserDTO userDTO, UserImDTO userImDTO) {
        if(null == userDTO || null == userImDTO){
            throw new BaseBizException(ProcessCodeEnum.USER_NOT_EXIST);
        }
        return new UserInfoDTO()
                .setAccId(userImDTO.getAccId())
                .setAvatar(userDTO.getAvatar())
                .setBirthday(userDTO.getBirthday())
                .setEmail(userDTO.getEmail())
                .setGender(userDTO.getGender())
                .setMobile(userDTO.getMobile())
                .setNickName(userDTO.getNickName())
                .setSummary(userDTO.getSummary())
                .setUid(userDTO.getUid())
                .setUserName(userDTO.getUserName())
                .setUserType(userDTO.getUserType());
    }

    private UserImDTO getUserImInfoByUid(String uid) {
        LambdaQueryWrapper<ChatAccountPO> queryWrapper = new LambdaQueryWrapper<ChatAccountPO>()
                .eq(ChatAccountPO::getUid, uid)
                .eq(ChatAccountPO::getAppId, AppIdEnum.KILA.appId);
        ChatAccountPO chatAccountPO = chatAccountMapper.selectOne(queryWrapper);
        if (null == chatAccountPO) {
            return null;
        }
        return new UserImDTO().setAccId(chatAccountPO.getAccId());
    }

    @Override
    public UserLoginResp userLogin(UserLoginReq req, HttpServletResponse resp) {
        LambdaQueryWrapper<UserPO> userPOLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userPOLambdaQueryWrapper
                .eq(UserPO::getUserName, req.getUserName())
                .eq(UserPO::getPwd, req.getPwd());
        UserPO userPO = userMapper.selectOne(userPOLambdaQueryWrapper);
        if (null == userPO) {
            log.error("当前用户不存在！");
            throw new BaseBizException(ProcessCodeEnum.USER_NOT_EXIST);
        }
        userCache.putValue(userPO.getUid(), userPO);
        UserDTO userDTO = UserConverter.convertUserPO2UserDTO(userPO);
        String token = TokenUtil.generateToken();
        // 缓存token
        tokenCache.putValue(token, userPO.getUid());
        resp.setHeader("w-token", token);
        UserImDTO userImDTO = getUserImInfoByUid(userDTO.getUid());
        return new UserLoginResp().setUserInfo(buildUserInfoDTO(userDTO,userImDTO));
    }

    @Override
    public SendVerificationCodeResp sendVerificationCode(SendVerificationCodeReq req) {
        String randomStr = RandomUtils.nextInt() + "";
        strCache.putValue(req.getEmail(), randomStr);
        String text = BizConstant.SUBJECT_VERIFICATION_CODE + "：" + randomStr;
        try {
            sendEmailUtil.sendMail(req.getEmail(), BizConstant.SUBJECT_VERIFICATION_CODE, text);
        } catch (Exception exception) {
            log.error("用户使用非邮箱登录，验证码发送失败！");
        }
        return new SendVerificationCodeResp().setVerificationCode(randomStr);
    }

    @Override
    public GetUserInfoByTokenResp getUserINfoByToken() {
        UserDTO userDTO = UserContext.getUserContext();
        if (null == userDTO) {
            throw new BaseBizException(ProcessCodeEnum.TOKEN_FAILURE);
        }
        String uid = userDTO.getUid();
        LambdaQueryWrapper<UserFollowPO> userFollowQueryWrapper = new LambdaQueryWrapper<>();
        userFollowQueryWrapper
                .eq(UserFollowPO::getFollowUid, uid);
        Integer followCount = userFollowMapper.selectCount(userFollowQueryWrapper);
        LambdaQueryWrapper<UserFollowPO> userFansQueryWrapper = new LambdaQueryWrapper<>();
        userFansQueryWrapper
                .eq(UserFollowPO::getBeFollowUid, uid);
        Integer fansCount = userFollowMapper.selectCount(userFansQueryWrapper);
        return new GetUserInfoByTokenResp().setUser(userDTO).
                setFollowCount(String.valueOf(followCount)).setFansCount(String.valueOf(fansCount));
    }

    @Override
    public UpdateUserResp updateUserInfo(UpdateUserReq req) {
        if (null == req) {
            throw new BaseBizException(ProcessCodeEnum.PARAM_ERROR);
        }
        UserDTO userDTO = UserContext.getUserContext();
        if (null == userDTO) {
            throw new BaseBizException(ProcessCodeEnum.TOKEN_FAILURE);
        }
        UserPO newUserPO = UserConverter.convertUpdateUserReq2UserPO(req);
        int successCount = userMapper.update(newUserPO,
                new LambdaUpdateWrapper<UserPO>().eq(UserPO::getUid, Long.valueOf(userDTO.getUid())));
        if (successCount != NumberConstant.ONE_POSITIVE) {
            throw new BaseBizException(ProcessCodeEnum.UPDATE_USER_INFO_ERROR);
        }
        UserPO userPO = userMapper.selectOne(new LambdaQueryWrapper<UserPO>().eq(UserPO::getUid, Long.valueOf(userDTO.getUid())));
        userCache.remove(userPO.getUid());
        userCache.putValue(userPO.getUid(), userPO);
        UserDTO newUserDTO = UserConverter.convertUserPO2UserDTO(userPO);
        String token = TokenUtil.generateToken();
        // 缓存token
        tokenCache.putValue(token, userPO.getUid());
        UserImDTO userImDTO = getUserImInfoByUid(userDTO.getUid());
        return new UpdateUserResp().setUserInfo(buildUserInfoDTO(newUserDTO,userImDTO));
    }

    @Override
    public UserResp retrievePwd(FindPwdReq req) {
        //1. 修改密码
        UserPO userPOUpdate = new UserPO()
                .setPwd(req.getNewPwd());
        LambdaUpdateWrapper<UserPO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(UserPO::getEmail, req.getEmail());
        int count = userMapper.update(userPOUpdate, updateWrapper);
        if (count > 0) {
            return new UserResp().setHintText("重设密码成功!");
        } else {
            log.error("重设密码失败！");
            throw new BaseBizException(ProcessCodeEnum.PWD_FIND_FAILED);
        }
    }

    @Override
    public UserResp checkVerificationCode(JudgeCodeReq req) {
        //1. 确认验证码是否正确
        checkVerificationCode(req.getVerificationCode());
        return new UserResp().setHintText("验证码正确");
    }

    @Override
    public List<UserItemDTO> searchUserByUidOrName(SearchUserReq req) {
        List<UserItemDTO> userItemDTOS = new ArrayList<>();
        String keyword = parseKeyword(req.getKeyword());
        List<UserPO> userPOList = userMapper.getUserLikeKeyword(keyword);
        //获取关注UID列表
        LambdaQueryWrapper<UserFollowPO> userFollowPOQueryWrapper = new LambdaQueryWrapper<>();
        userFollowPOQueryWrapper
                .eq(UserFollowPO::getFollowUid, req.getUid());
        List<UserFollowPO> userFollowPOList = userFollowMapper.selectList(userFollowPOQueryWrapper);
        List<Long> followUidList =
                userFollowPOList.stream().map(UserFollowPO::getBeFollowUid).collect(Collectors.toList());
        for (UserPO userPO : userPOList) {
            UserItemDTO userItemDTO = new UserItemDTO(userPO);
            if(followUidList.contains(userPO.getUid())){
                userItemDTO.setFollowStatus(UserFollowStatus.FOLLOW.getType());
            }else {
                userItemDTO.setFollowStatus(UserFollowStatus.NO_FOLLOW.getType());
            }
            userItemDTOS.add(userItemDTO);
        }
        return userItemDTOS;
    }

    @Override
    public UserResp updateUserType(UserTypeReq req) {
        UserPO userPO = userMapper.selectOne
                (new LambdaQueryWrapper<UserPO>().eq(UserPO::getEmail, req.getEmail()));
        if(userPO == null){
            log.error("当前用户不存在！");
            throw new BaseBizException(ProcessCodeEnum.USER_NOT_EXIST);
        }
        // TODO fix
        if(userPO.getUserType() == req.getUserType()){
            return new UserResp().setHintText("当前用户类型已被修改!");
        }
        UserPO userPOUpdate = new UserPO()
                .setUserType(req.getUserType());
        LambdaUpdateWrapper<UserPO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(UserPO::getEmail, req.getEmail());
        int count = userMapper.update(userPOUpdate, updateWrapper);
        if (count > 0) {
            return new UserResp().setHintText("修改用户类型成功!");
        } else {
            log.error("修改用户类型失败！");
            throw new BaseBizException(ProcessCodeEnum.USER_TYPE_FAILED);
        }
    }

    //检查验证码
    private void checkVerificationCode(String verificationCode) {
        if (!Objects.equals(verificationCode, "6666")) {
            String cacheVerificationCode = strCache.remove("", String.class);
            if (StringUtils.isBlank(verificationCode)) {
                log.error("验证码错误！");
                throw new BaseBizException(ProcessCodeEnum.VERIFICATION_CODE_NOT_MATCH);
            }
            if (!Objects.equals(verificationCode, cacheVerificationCode)) {
                log.error("验证码错误！");
                throw new BaseBizException(ProcessCodeEnum.VERIFICATION_CODE_NOT_MATCH);
            }
        }
    }

    //检查邮箱
    private void checkEmail(String email){
        LambdaQueryWrapper<UserPO> userPOLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userPOLambdaQueryWrapper
                .eq(UserPO::getEmail, email);
        UserPO userPO = userMapper.selectOne(userPOLambdaQueryWrapper);
        if (userPO == null) {
            log.error("当前邮箱不存在请检查！");
            throw new BaseBizException(ProcessCodeEnum.EMAIL_NOT_EXIST);
        }
    }

    public static boolean isNumber(String str) {
        Pattern pattern = Pattern.compile("[0-9]*");
        return pattern.matcher(str).matches();
    }

    public static String parseKeyword(String keyword) {
        if (StringUtils.isEmpty(keyword)) {
            return "";
        }
        return "%" + keyword + "%";
    }

}
